package com.xlh.virtualization.controller;

import com.google.common.base.Stopwatch;
import com.xlh.aop.PreAcquireLock;
import com.xlh.common.Result;
import com.xlh.exception.common.GlobalException;
import com.xlh.security.SpringSecurityUserInfoHolder;
import com.xlh.security.UserInfoHolder;
import com.xlh.support.training.domain.Training;
import com.xlh.support.training.service.TrainingService;
import com.xlh.virtualization.domain.ContainerContext;
import com.xlh.virtualization.domain.ExperimentContainer;
import com.xlh.virtualization.domain.ExperimentContext;
import com.xlh.virtualization.po.FlavorUpdatePO;
import com.xlh.virtualization.properties.ContainerProperties;
import com.xlh.virtualization.service.ContainerService;
import com.xlh.virtualization.service.ExperimentContainerService;
import com.xlh.virtualization.service.ExperimentService;
import com.xlh.virtualization.service.ImageAllocationService;
import com.xlh.virtualization.vo.ExperimentVO;
import com.xlh.virtualization.vo.FlavorVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import static com.xlh.virtualization.constant.ContextTypeEnum.TRAINING;
import static com.xlh.virtualization.constant.ExperimentConstant.*;

/**
 * @author cheer
 */
@Api("实训实验")
@RestController
@RequestMapping("/experiment/training")
public class ExperimentTrainingController extends BaseExperimentController {

    @Autowired
    public ExperimentTrainingController(UserInfoHolder userInfoHolder, ExperimentService experimentService,
                                        ContainerService containerService) {
        super.userInfoHolder = userInfoHolder;
        super.experimentService = experimentService;
        super.containerService = containerService;
    }

    @Autowired
    private ContainerProperties containerProperties;

    @Autowired
    private ImageAllocationService imageAllocationService;

    @Autowired
    private TrainingService trainingService;

    @Autowired
    private ExperimentContainerService experimentContainerService;

    @ApiOperation("实训中查询开始实验提示（是否有其它实验正在进行）")
    @GetMapping("/{trainingId}/prompt")
    @PreAuthorize("@permissionValidator.isTeacherOrStudent()")
    public Result<Boolean> queryPrompt(@PathVariable("trainingId") Long trainingId) {
        ContainerContext containerContext = getContainerContext(userInfoHolder.getUserId(), trainingId);
        if (experimentService.isOtherExperimentRunning(containerContext)) {
            return Result.success(true);
        }
        return Result.success(false);
    }

    @ApiOperation("实训中进入实验（获取虚拟机）/ 刷新")
    @GetMapping("/{trainingId}")
    @PreAuthorize("@permissionValidator.isTeacherOrStudent()")
    @PreAcquireLock(value = ENTER_EXPERIMENT_KEY, timeout = ENTER_EXPERIMENT_TIME_OUT,
            message = ENTER_EXPERIMENT_LOCK_MESSAGE)
    public Result<ExperimentVO> enterExperiment(@PathVariable("trainingId") Long trainingId,
                                                @Autowired SpringSecurityUserInfoHolder userInfoHolder) {
        return super.enterExperiment(trainingId);
    }

    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    @ApiOperation("实训中进入实验（获取虚拟机）/ 刷新")
    @GetMapping("/test/{trainingId}/start")
    @PreAuthorize("@permissionValidator.isAdminOrTeacher()")
    public Result teststart(@PathVariable("trainingId") Long trainingId, @RequestParam Integer number) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(number);

        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < number; i++) {
            int finalI = i;
            taskExecutor.execute(() -> {
                try {
                    Stopwatch stopwatch = Stopwatch.createStarted();
                    ContainerContext containerContext = new ContainerContext()
                            .setUserId((long) finalI)
                            .setContextType(TRAINING.getCode())
                            .setContextId(trainingId);
                    ExperimentContext experimentContext = new ExperimentContext()
                            .setUserId((long) finalI)
                            .setContextType(TRAINING.getCode())
                            .setContextId(trainingId);
                    experimentService.getExperiment(containerContext, experimentContext, null);
                    list.add((int) stopwatch.elapsed(TimeUnit.SECONDS));
                } catch (Exception e) {
                    e.printStackTrace();
                    list.add((-1));
                }
                countDownLatch.countDown();
            });
        }

        countDownLatch.await(10, TimeUnit.MINUTES);

        return Result.success(list);
    }

    @ApiOperation("实训中进入实验（获取虚拟机）/ 刷新")
    @GetMapping("/test/{trainingId}/stop")
    @PreAuthorize("@permissionValidator.isAdminOrTeacher()")
    public Result teststop(@PathVariable("trainingId") Long trainingId, @RequestParam Integer number) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(number);

        for (int i = 0; i < number; i++) {
            int finalI = i;
            taskExecutor.execute(() -> {
                ContainerContext containerContext = new ContainerContext()
                        .setUserId((long) finalI)
                        .setContextType(TRAINING.getCode())
                        .setContextId(trainingId);
                containerService.deleteContainerByContext(containerContext);
                countDownLatch.countDown();
            });
        }

        countDownLatch.await(10, TimeUnit.MINUTES);

        return Result.success();
    }

    @ApiOperation("实训中初始化实验")
    @GetMapping("/{trainingId}/init")
    @PreAuthorize("@permissionValidator.isTeacherOrStudent()")
    @PreAcquireLock(value = ENTER_EXPERIMENT_KEY, timeout = ENTER_EXPERIMENT_TIME_OUT,
            message = ENTER_EXPERIMENT_LOCK_MESSAGE)
    public Result<ExperimentVO> initExperiment(@PathVariable("trainingId") Long trainingId,
                                               @Autowired SpringSecurityUserInfoHolder userInfoHolder) {
        return super.initExperiment(trainingId);
    }

    @ApiOperation("实训中查询配置")
    @GetMapping("/{trainingId}/flavor")
    @PreAuthorize("@permissionValidator.isTeacher()")
    public Result<FlavorVO> queryFlavor(@PathVariable("trainingId") Long trainingId) {
        FlavorVO flavorVO = imageAllocationService.getFlavorByContext(TRAINING.getCode(), trainingId);
        return Result.success(flavorVO);
    }

    @ApiOperation("实训中保存配置")
    @PostMapping("/{trainingId}/flavor")
    @PreAuthorize("@permissionValidator.isTeacher()")
    public Result updateFlavor(@PathVariable("trainingId") Long trainingId,
                               @RequestBody @Valid FlavorUpdatePO flavorUpdatePO) {
        imageAllocationService.updateFlavorByContext(TRAINING.getCode(), trainingId, flavorUpdatePO);
        return Result.success();
    }

    @ApiOperation("实训中压缩文件")
    @GetMapping("/{trainingId}/zip")
    @PreAuthorize("@permissionValidator.isTeacherOrStudent()")
    public Result zipFile(@PathVariable("trainingId") Long trainingId) {
        ContainerContext containerContext = getContainerContext(userInfoHolder.getUserId(), trainingId);
        String filePath = getVolumePath(containerContext);
        experimentContainerService.zipFile(filePath);
        return Result.success();
    }

    @ApiOperation("实训中下载文件")
    @GetMapping("/{trainingId}/download")
    @PreAuthorize("@permissionValidator.isTeacherOrStudent()")
    public void downloadFile(@PathVariable("trainingId") Long trainingId) {
        Training training = trainingService.getTraining(trainingId);
        if (training == null) {
            throw new GlobalException("未找到环境实训信息");
        }

        ContainerContext containerContext = getContainerContext(userInfoHolder.getUserId(), trainingId);
        String filePath = getVolumePath(containerContext);
        String fileName = userInfoHolder.getUsername() + "-" + training.getTrainingName() + "-代码.zip";
        experimentContainerService.downloadFile(filePath, fileName);
    }

    @Override
    protected ContainerContext getContainerContext(Long userId, Long contextId) {
        return new ContainerContext()
                .setUserId(userId)
                .setContextType(TRAINING.getCode())
                .setContextId(contextId);
    }

    @Override
    protected ExperimentContext getExperimentContext(Long userId, Long contextId) {
        return new ExperimentContext()
                .setUserId(userId)
                .setContextType(TRAINING.getCode())
                .setContextId(contextId);
    }

    @Override
    protected void preEnterExperiment(ContainerContext containerContext, ExperimentContext experimentContext) {
        experimentService.stopOtherExperiment(containerContext);
    }

    @Override
    protected void postEnterExperiment(ContainerContext containerContext, ExperimentContext experimentContext,
                                       List<ExperimentContainer> experimentContainers) {

        try {
            String path = getVolumePath(containerContext);
            new File(path).setWritable(true, false);
        } catch (Exception ignore) {
        }
    }

    @Override
    protected String getVolumePath(ContainerContext containerContext) {
        return containerProperties.getNfsDir() + "/training/t" + containerContext.getContextId() + "/u" +
                containerContext.getUserId() + "/";
    }

}
